home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mgr / sparcmgr / demo2.zoo / demo / ex / ex_cmdsub.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-24  |  24.1 KB  |  1,293 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *sccsid = "@(#)ex_cmdsub.c    7.8 (Berkeley) 3/9/87; 1.2 (Bellcore)    87/04/24";
  9. #endif not lint
  10.  
  11. #include "ex.h"
  12. #include "ex_argv.h"
  13. #include "ex_temp.h"
  14. #include "ex_tty.h"
  15. #include "ex_vis.h"
  16.  
  17. /*
  18.  * Command mode subroutines implementing
  19.  *    append, args, copy, delete, join, move, put,
  20.  *    shift, tag, yank, z and undo
  21.  */
  22.  
  23. bool    endline = 1;
  24. line    *tad1;
  25. static    jnoop();
  26.  
  27. /*
  28.  * Append after line a lines returned by function f.
  29.  * Be careful about intermediate states to avoid scramble
  30.  * if an interrupt comes in.
  31.  */
  32. append(f, a)
  33.     int (*f)();
  34.     line *a;
  35. {
  36.     register line *a1, *a2, *rdot;
  37.     int nline;
  38.  
  39.     nline = 0;
  40.     dot = a;
  41.     if(FIXUNDO && !inopen && f!=getsub) {
  42.         undap1 = undap2 = dot + 1;
  43.         undkind = UNDCHANGE;
  44.     }
  45.     while ((*f)() == 0) {
  46.         if (truedol >= endcore) {
  47.             if (morelines() < 0) {
  48.                 if (FIXUNDO && f == getsub) {
  49.                     undap1 = addr1;
  50.                     undap2 = addr2 + 1;
  51.                 }
  52.                 error("Out of memory@- too many lines in file");
  53.             }
  54.         }
  55.         nline++;
  56.         a1 = truedol + 1;
  57.         a2 = a1 + 1;
  58.         dot++;
  59.         undap2++;
  60.         dol++;
  61.         unddol++;
  62.         truedol++;
  63.         for (rdot = dot; a1 > rdot;)
  64.             *--a2 = *--a1;
  65.         *rdot = 0;
  66.         putmark(rdot);
  67.         if (f == gettty) {
  68.             dirtcnt++;
  69.             TSYNC();
  70.         }
  71.     }
  72.     return (nline);
  73. }
  74.  
  75. appendnone()
  76. {
  77.  
  78.     if(FIXUNDO) {
  79.         undkind = UNDCHANGE;
  80.         undap1 = undap2 = addr1;
  81.     }
  82. }
  83.  
  84. /*
  85.  * Print out the argument list, with []'s around the current name.
  86.  */
  87. pargs()
  88. {
  89.     register char **av = argv0, *as = args0;
  90.     register int ac;
  91.  
  92.     for (ac = 0; ac < argc0; ac++) {
  93.         if (ac != 0)
  94.             ex_putchar(' ' | QUOTE);
  95.         if (ac + argc == argc0 - 1)
  96.             ex_printf("[");
  97.         lprintf("%s", as);
  98.         if (ac + argc == argc0 - 1)
  99.             ex_printf("]");
  100.         as = av ? *++av : strend(as) + 1;
  101.     }
  102.     noonl();
  103. }
  104.  
  105. /*
  106.  * Delete lines; two cases are if we are really deleting,
  107.  * more commonly we are just moving lines to the undo save area.
  108.  */
  109. ex_delete(hush)
  110.     bool hush;
  111. {
  112.     register line *a1, *a2;
  113.  
  114.     nonzero();
  115.     if(FIXUNDO) {
  116.         register int (*dsavint)();
  117.  
  118. #ifdef TRACE
  119.         if (trace)
  120.             vudump("before delete");
  121. #endif
  122.         change();
  123.         dsavint = signal(SIGINT, SIG_IGN);
  124.         undkind = UNDCHANGE;
  125.         a1 = addr1;
  126.         squish();
  127.         a2 = addr2;
  128.         if (a2++ != dol) {
  129.             reverse(a1, a2);
  130.             reverse(a2, dol + 1);
  131.             reverse(a1, dol + 1);
  132.         }
  133.         dol -= a2 - a1;
  134.         unddel = a1 - 1;
  135.         if (a1 > dol)
  136.             a1 = dol;
  137.         dot = a1;
  138.         pkill[0] = pkill[1] = 0;
  139.         signal(SIGINT, dsavint);
  140. #ifdef TRACE
  141.         if (trace)
  142.             vudump("after delete");
  143. #endif
  144.     } else {
  145.         register line *a3;
  146.         register int i;
  147.  
  148.         change();
  149.         a1 = addr1;
  150.         a2 = addr2 + 1;
  151.         a3 = truedol;
  152.         i = a2 - a1;
  153.         unddol -= i;
  154.         undap2 -= i;
  155.         dol -= i;
  156.         truedol -= i;
  157.         do
  158.             *a1++ = *a2++;
  159.         while (a2 <= a3);
  160.         a1 = addr1;
  161.         if (a1 > dol)
  162.             a1 = dol;
  163.         dot = a1;
  164.     }
  165.     if (!hush)
  166.         killed();
  167. }
  168.  
  169. deletenone()
  170. {
  171.  
  172.     if(FIXUNDO) {
  173.         undkind = UNDCHANGE;
  174.         squish();
  175.         unddel = addr1;
  176.     }
  177. }
  178.  
  179. /*
  180.  * Crush out the undo save area, moving the open/visual
  181.  * save area down in its place.
  182.  */
  183. squish()
  184. {
  185.     register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
  186.  
  187.     if(FIXUNDO) {
  188.         if (inopen == -1)
  189.             return;
  190.         if (a1 < a2 && a2 < a3)
  191.             do
  192.                 *a1++ = *a2++;
  193.             while (a2 < a3);
  194.         truedol -= unddol - dol;
  195.         unddol = dol;
  196.     }
  197. }
  198.  
  199. /*
  200.  * Join lines.  Special hacks put in spaces, two spaces if
  201.  * preceding line ends with '.', or no spaces if next line starts with ).
  202.  */
  203. static    int jcount, jnoop();
  204.  
  205. join(c)
  206.     int c;
  207. {
  208.     register line *a1;
  209.     register char *cp, *cp1;
  210.  
  211.     cp = genbuf;
  212.     *cp = 0;
  213.     for (a1 = addr1; a1 <= addr2; a1++) {
  214.         getline(*a1);
  215.         cp1 = linebuf;
  216.         if (a1 != addr1 && c == 0) {
  217.             while (*cp1 == ' ' || *cp1 == '\t')
  218.                 cp1++;
  219.             if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
  220.                 if (*cp1 != ')') {
  221.                     *cp++ = ' ';
  222.                     if (cp[-2] == '.')
  223.                         *cp++ = ' ';
  224.                 }
  225.             }
  226.         }
  227.         while (*cp++ = *cp1++)
  228.             if (cp > &genbuf[LBSIZE-2])
  229.                 error("Line overflow|Result line of join would be too long");
  230.         cp--;
  231.     }
  232.     strcLIN(genbuf);
  233.     ex_delete(0);
  234.     jcount = 1;
  235.     if (FIXUNDO)
  236.         undap1 = undap2 = addr1;
  237.     ignore(append(jnoop, --addr1));
  238.     if (FIXUNDO)
  239.         vundkind = VMANY;
  240. }
  241.  
  242. static
  243. jnoop()
  244. {
  245.  
  246.     return(--jcount);
  247. }
  248.  
  249. /*
  250.  * Move and copy lines.  Hard work is done by move1 which
  251.  * is also called by undo.
  252.  */
  253. int    getcopy();
  254.  
  255. move()
  256. {
  257.     register line *adt;
  258.     bool iscopy = 0;
  259.  
  260.     if (Command[0] == 'm') {
  261.         setdot1();
  262.         markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
  263.     } else {
  264.         iscopy++;
  265.         setdot();
  266.     }
  267.     nonzero();
  268.     adt = address((char*)0);
  269.     if (adt == 0)
  270.         serror("%s where?|%s requires a trailing address", Command);
  271.     newline();
  272.     move1(iscopy, adt);
  273.     killed();
  274. }
  275.  
  276. move1(cflag, addrt)
  277.     int cflag;
  278.     line *addrt;
  279. {
  280.     register line *adt, *ad1, *ad2;
  281.     int lines;
  282.  
  283.     adt = addrt;
  284.     lines = (addr2 - addr1) + 1;
  285.     if (cflag) {
  286.         tad1 = addr1;
  287.         ad1 = dol;
  288.         ignore(append(getcopy, ad1++));
  289.         ad2 = dol;
  290.     } else {
  291.         ad2 = addr2;
  292.         for (ad1 = addr1; ad1 <= ad2;)
  293.             *ad1++ &= ~01;
  294.         ad1 = addr1;
  295.     }
  296.     ad2++;
  297.     if (adt < ad1) {
  298.         if (adt + 1 == ad1 && !cflag && !inglobal)
  299.             error("That move would do nothing!");
  300.         dot = adt + (ad2 - ad1);
  301.         if (++adt != ad1) {
  302.             reverse(adt, ad1);
  303.             reverse(ad1, ad2);
  304.             reverse(adt, ad2);
  305.         }
  306.     } else if (adt >= ad2) {
  307.         dot = adt++;
  308.         reverse(ad1, ad2);
  309.         reverse(ad2, adt);
  310.         reverse(ad1, adt);
  311.     } else
  312.         error("Move to a moved line");
  313.     change();
  314.     if (!inglobal)
  315.         if(FIXUNDO) {
  316.             if (cflag) {
  317.                 undap1 = addrt + 1;
  318.                 undap2 = undap1 + lines;
  319.                 deletenone();
  320.             } else {
  321.                 undkind = UNDMOVE;
  322.                 undap1 = addr1;
  323.                 undap2 = addr2;
  324.                 unddel = addrt;
  325.                 squish();
  326.             }
  327.         }
  328. }
  329.  
  330. getcopy()
  331. {
  332.  
  333.     if (tad1 > addr2)
  334.         return (EOF);
  335.     getline(*tad1++);
  336.     return (0);
  337. }
  338.  
  339. /*
  340.  * Put lines in the buffer from the undo save area.
  341.  */
  342. getput()
  343. {
  344.  
  345.     if (tad1 > unddol)
  346.         return (EOF);
  347.     getline(*tad1++);
  348.     tad1++;
  349.     return (0);
  350. }
  351.  
  352. put()
  353. {
  354.     register int cnt;
  355.  
  356.     if (!FIXUNDO)
  357.         error("Cannot put inside global/macro");
  358.     cnt = unddol - dol;
  359.     if (cnt && inopen && pkill[0] && pkill[1]) {
  360.         pragged(1);
  361.         return;
  362.     }
  363.     tad1 = dol + 1;
  364.     ignore(append(getput, addr2));
  365.     undkind = UNDPUT;
  366.     notecnt = cnt;
  367.     netchange(cnt);
  368. }
  369.  
  370. /*
  371.  * A tricky put, of a group of lines in the middle
  372.  * of an existing line.  Only from open/visual.
  373.  * Argument says pkills have meaning, e.g. called from
  374.  * put; it is 0 on calls from putreg.
  375.  */
  376. pragged(kill)
  377.     bool kill;
  378. {
  379.     extern char *cursor;
  380.     register char *gp = &genbuf[cursor - linebuf];
  381.  
  382.     /*
  383.      * This kind of stuff is TECO's forte.
  384.      * We just grunge along, since it cuts
  385.      * across our line-oriented model of the world
  386.      * almost scrambling our addled brain.
  387.      */
  388.     if (!kill)
  389.         getDOT();
  390.     strcpy(genbuf, linebuf);
  391.     getline(*unddol);
  392.     if (kill)
  393.         *pkill[1] = 0;
  394.     strcat(linebuf, gp);
  395.     putmark(unddol);
  396.     getline(dol[1]);
  397.     if (kill)
  398.         strcLIN(pkill[0]);
  399.     strcpy(gp, linebuf);
  400.     strcLIN(genbuf);
  401.     putmark(dol+1);
  402.     undkind = UNDCHANGE;
  403.     undap1 = dot;
  404.     undap2 = dot + 1;
  405.     unddel = dot - 1;
  406.     undo(1);
  407. }
  408.  
  409. /*
  410.  * Shift lines, based on c.
  411.  * If c is neither < nor >, then this is a lisp aligning =.
  412.  */
  413. shift(c, cnt)
  414.     int c;
  415.     int cnt;
  416. {
  417.     register line *addr;
  418.     register char *cp;
  419.     char *dp;
  420.     register int i;
  421.  
  422.     if(FIXUNDO)
  423.         save12(), undkind = UNDCHANGE;
  424.     cnt *= value(SHIFTWIDTH);
  425.     for (addr = addr1; addr <= addr2; addr++) {
  426.         dot = addr;
  427. #ifdef LISPCODE
  428.         if (c == '=' && addr == addr1 && addr != addr2)
  429.             continue;
  430. #endif
  431.         getDOT();
  432.         i = whitecnt(linebuf);
  433.         switch (c) {
  434.  
  435.         case '>':
  436.             if (linebuf[0] == 0)
  437.                 continue;
  438.             cp = genindent(i + cnt);
  439.             break;
  440.  
  441.         case '<':
  442.             if (i == 0)
  443.                 continue;
  444.             i -= cnt;
  445.             cp = i > 0 ? genindent(i) : genbuf;
  446.             break;
  447.  
  448. #ifdef LISPCODE
  449.         default:
  450.             i = lindent(addr);
  451.             getDOT();
  452.             cp = genindent(i);
  453.             break;
  454. #endif
  455.         }
  456.         if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
  457.             error("Line too long|Result line after shift would be too long");
  458.         CP(cp, dp);
  459.         strcLIN(genbuf);
  460.         putmark(addr);
  461.     }
  462.     killed();
  463. }
  464.  
  465. /*
  466.  * Find a tag in the tags file.
  467.  * Most work here is in parsing the tags file itself.
  468.  */
  469. tagfind(quick)
  470.     bool quick;
  471. {
  472.     char cmdbuf[BUFSIZ];
  473.     char filebuf[FNSIZE];
  474.     char tagfbuf[128];
  475.     register int c, d;
  476.     bool samef = 1;
  477.     int tfcount = 0;
  478.     int omagic;
  479.     char *fn, *fne;
  480.     struct stat sbuf;
  481. #ifdef FASTTAG
  482.     int iof;
  483.     char iofbuf[MAXBSIZE];
  484.     long mid;    /* assumed byte offset */
  485.     long top, bot;    /* length of tag file */
  486. #endif
  487.  
  488.     omagic = value(MAGIC);
  489.     if (!skipend()) {
  490.         register char *lp = lasttag;
  491.  
  492.         while (!iswhite(peekchar()) && !endcmd(peekchar()))
  493.             if (lp < &lasttag[sizeof lasttag - 2])
  494.                 *lp++ = ex_getchar();
  495.             else
  496.                 ignchar();
  497.         *lp++ = 0;
  498.         if (!endcmd(peekchar()))
  499. badtag:
  500.             error("Bad tag|Give one tag per line");
  501.     } else if (lasttag[0] == 0)
  502.         error("No previous tag");
  503.     c = ex_getchar();
  504.     if (!endcmd(c))
  505.         goto badtag;
  506.     if (c == EOF)
  507.         ungetchar(c);
  508.     clrstats();
  509.  
  510.     /*
  511.      * Loop once for each file in tags "path".
  512.      */
  513.     CP(tagfbuf, svalue(TAGS));
  514.     fne = tagfbuf - 1;
  515.     while (fne) {
  516.         fn = ++fne;
  517.         while (*fne && *fne != ' ')
  518.             fne++;
  519.         if (*fne == 0)
  520.             fne = 0;    /* done, quit after this time */
  521.         else
  522.             *fne = 0;    /* null terminate filename */
  523. #ifdef FASTTAG
  524.         iof = topen(fn, iofbuf);
  525.         if (iof == -1)
  526.             continue;
  527.         tfcount++;
  528.         fstat(iof, &sbuf);
  529.         top = sbuf.st_size;
  530.         if (top == 0L )
  531.             top = -1L;
  532.         bot = 0L;
  533.         while (top >= bot) {
  534. #else
  535.         /*
  536.          * Avoid stdio and scan tag file linearly.
  537.          */
  538.         io = open(fn, 0);
  539.         if (io<0)
  540.             continue;
  541.         tfcount++;
  542.         if (fstat(io, &sbuf) < 0)
  543.             bsize = LBSIZE;
  544.         else {
  545.             bsize = sbuf.st_blksize;
  546.             if (bsize <= 0)
  547.                 bsize = LBSIZE;
  548.         }
  549.         while (getfile() == 0) {
  550. #endif
  551.             /* loop for each tags file entry */
  552.             register char *cp = linebuf;
  553.             register char *lp = lasttag;
  554.             char *oglobp;
  555.  
  556. #ifdef FASTTAG
  557.             mid = (top + bot) / 2;
  558.             tseek(iof, mid);
  559.             if (mid > 0)    /* to get first tag in file to work */
  560.                 /* scan to next \n */
  561.                 if(tgets(linebuf, sizeof linebuf, iof)==NULL)
  562.                     goto goleft;
  563.             /* get the line itself */
  564.             if(tgets(linebuf, sizeof linebuf, iof)==NULL)
  565.                 goto goleft;
  566. #ifdef TDEBUG
  567.             ex_printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
  568. #endif
  569. #endif
  570.             while (*cp && *lp == *cp)
  571.                 cp++, lp++;
  572.             if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 ||
  573.                 lp-lasttag < value(TAGLENGTH))) {
  574. #ifdef FASTTAG
  575.                 if (*lp > *cp)
  576.                     bot = mid + 1;
  577.                 else
  578. goleft:
  579.                     top = mid - 1;
  580. #endif
  581.                 /* Not this tag.  Try the next */
  582.                 continue;
  583.             }
  584.  
  585.             /*
  586.              * We found the tag.  Decode the line in the file.
  587.              */
  588. #ifdef FASTTAG
  589.             tclose(iof);
  590. #else
  591.             close(io);
  592. #endif
  593.             /* Rest of tag if abbreviated */
  594.             while (!iswhite(*cp))
  595.                 cp++;
  596.  
  597.             /* name of file */
  598.             while (*cp && iswhite(*cp))
  599.                 cp++;
  600.             if (!*cp)
  601. badtags:
  602.                 serror("%s: Bad tags file entry", lasttag);
  603.             lp = filebuf;
  604.             while (*cp && *cp != ' ' && *cp != '\t') {
  605.                 if (lp < &filebuf[sizeof filebuf - 2])
  606.                     *lp++ = *cp;
  607.                 cp++;
  608.             }
  609.             *lp++ = 0;
  610.  
  611.             if (*cp == 0)
  612.                 goto badtags;
  613.             if (dol != zero) {
  614.                 /*
  615.                  * Save current position in 't for ^^ in visual.
  616.                  */
  617.                 names['t'-'a'] = *dot &~ 01;
  618.                 if (inopen) {
  619.                     extern char *ncols['z'-'a'+2];
  620.                     extern char *cursor;
  621.  
  622.                     ncols['t'-'a'] = cursor;
  623.                 }
  624.             }
  625.             strcpy(cmdbuf, cp);
  626.             if (strcmp(filebuf, savedfile) || !edited) {
  627.                 char cmdbuf2[sizeof filebuf + 10];
  628.  
  629.                 /* Different file.  Do autowrite & get it. */
  630.                 if (!quick) {
  631.                     ckaw();
  632.                     if (chng && dol > zero)
  633.                         error("No write@since last change (:tag! overrides)");
  634.                 }
  635.                 oglobp = globp;
  636.                 strcpy(cmdbuf2, "e! ");
  637.                 strcat(cmdbuf2, filebuf);
  638.                 globp = cmdbuf2;
  639.                 d = peekc; ungetchar(0);
  640.                 commands(1, 1);
  641.                 peekc = d;
  642.                 globp = oglobp;
  643.                 value(MAGIC) = omagic;
  644.                 samef = 0;
  645.             }
  646.  
  647.             /*
  648.              * Look for pattern in the current file.
  649.              */
  650.             oglobp = globp;
  651.             globp = cmdbuf;
  652.             d = peekc; ungetchar(0);
  653.             if (samef)
  654.                 markpr(dot);
  655.             /*
  656.              * BUG: if it isn't found (user edited header
  657.              * line) we get left in nomagic mode.
  658.              */
  659.             value(MAGIC) = 0;
  660.             commands(1, 1);
  661.             peekc = d;
  662.             globp = oglobp;
  663.             value(MAGIC) = omagic;
  664.             return;
  665.         }    /* end of "for each tag in file" */
  666.  
  667.         /*
  668.          * No such tag in this file.  Close it and try the next.
  669.          */
  670. #ifdef FASTTAG
  671.         tclose(iof);
  672. #else
  673.         close(io);
  674. #endif
  675.     }    /* end of "for each file in path" */
  676.     if (tfcount <= 0)
  677.         error("No tags file");
  678.     else
  679.         serror("%s: No such tag@in tags file", lasttag);
  680. }
  681.  
  682. /*
  683.  * Save lines from addr1 thru addr2 as though
  684.  * they had been deleted.
  685.  */
  686. yank()
  687. {
  688.  
  689.     if (!FIXUNDO)
  690.         error("Can't yank inside global/macro");
  691.     save12();
  692.     undkind = UNDNONE;
  693.     killcnt(addr2 - addr1 + 1);
  694. }
  695.  
  696. /*
  697.  * z command; print windows of text in the file.
  698.  *
  699.  * If this seems unreasonably arcane, the reasons
  700.  * are historical.  This is one of the first commands
  701.  * added to the first ex (then called en) and the
  702.  * number of facilities here were the major advantage
  703.  * of en over ed since they allowed more use to be
  704.  * made of fast terminals w/o typing .,.22p all the time.
  705.  */
  706. bool    zhadpr;
  707. bool    znoclear;
  708. short    zweight;
  709.  
  710. zop(hadpr)
  711.     int hadpr;
  712. {
  713.     register int c, lines, op;
  714.     bool excl;
  715.  
  716.     zhadpr = hadpr;
  717.     notempty();
  718.     znoclear = 0;
  719.     zweight = 0;
  720.     excl = exclam();
  721.     switch (c = op = ex_getchar()) {
  722.  
  723.     case '^':
  724.         zweight = 1;
  725.     case '-':
  726.     case '+':
  727.         while (peekchar() == op) {
  728.             ignchar();
  729.             zweight++;
  730.         }
  731.     case '=':
  732.     case '.':
  733.         c = ex_getchar();
  734.         break;
  735.  
  736.     case EOF:
  737.         znoclear++;
  738.         break;
  739.  
  740.     default:
  741.         op = 0;
  742.         break;
  743.     }
  744.     if (isdigit(c)) {
  745.         lines = c - '0';
  746.         for(;;) {
  747.             c = ex_getchar();
  748.             if (!isdigit(c))
  749.                 break;
  750.             lines *= 10;
  751.             lines += c - '0';
  752.         }
  753.         if (lines < LINES)
  754.             znoclear++;
  755.         value(WINDOW) = lines;
  756.         if (op == '=')
  757.             lines += 2;
  758.     } else
  759.         lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
  760.     if (inopen || c != EOF) {
  761.         ungetchar(c);
  762.         newline();
  763.     }
  764.     addr1 = addr2;
  765.     if (addr2 == 0 && dot < dol && op == 0)
  766.         addr1 = addr2 = dot+1;
  767.     setdot();
  768.     zop2(lines, op);
  769. }
  770.  
  771. zop2(lines, op)
  772.     register int lines;
  773.     register int op;
  774. {
  775.     register line *split;
  776.  
  777.     split = NULL;
  778.     switch (op) {
  779.  
  780.     case EOF:
  781.         if (addr2 == dol)
  782.             error("\nAt EOF");
  783.     case '+':
  784.         if (addr2 == dol)
  785.             error("At EOF");
  786.         addr2 += lines * zweight;
  787.         if (addr2 > dol)
  788.             error("Hit BOTTOM");
  789.         addr2++;
  790.     default:
  791.         addr1 = addr2;
  792.         addr2 += lines-1;
  793.         dot = addr2;
  794.         break;
  795.  
  796.     case '=':
  797.     case '.':
  798.         znoclear = 0;
  799.         lines--;
  800.         lines >>= 1;
  801.         if (op == '=')
  802.             lines--;
  803.         addr1 = addr2 - lines;
  804.         if (op == '=')
  805.             dot = split = addr2;
  806.         addr2 += lines;
  807.         if (op == '.') {
  808.             markDOT();
  809.             dot = addr2;
  810.         }
  811.         break;
  812.  
  813.     case '^':
  814.     case '-':
  815.         addr2 -= lines * zweight;
  816.         if (addr2 < one)
  817.             error("Hit TOP");
  818.         lines--;
  819.         addr1 = addr2 - lines;
  820.         dot = addr2;
  821.         break;
  822.     }
  823.     if (addr1 <= zero)
  824.         addr1 = one;
  825.     if (addr2 > dol)
  826.         addr2 = dol;
  827.     if (dot > dol)
  828.         dot = dol;
  829.     if (addr1 > addr2)
  830.         return;
  831.     if (op == EOF && zhadpr) {
  832.         getline(*addr1);
  833.         ex_putchar('\r' | QUOTE);
  834.         shudclob = 1;
  835.     } else if (znoclear == 0 && CL != NOSTR && !inopen) {
  836.         flush1();
  837.         vclear();
  838.     }
  839.     if (addr2 - addr1 > 1)
  840.         pstart();
  841.     if (split) {
  842.         plines(addr1, split - 1, 0);
  843.         splitit();
  844.         plines(split, split, 0);
  845.         splitit();
  846.         addr1 = split + 1;
  847.     }
  848.     plines(addr1, addr2, 0);
  849. }
  850.  
  851. static
  852. splitit()
  853. {
  854.     register int l;
  855.  
  856.     for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
  857.         ex_putchar('-');
  858.     putnl();
  859. }
  860.  
  861. plines(adr1, adr2, movedot)
  862.     line *adr1;
  863.     register line *adr2;
  864.     bool movedot;
  865. {
  866.     register line *addr;
  867.  
  868.     pofix();
  869.     for (addr = adr1; addr <= adr2; addr++) {
  870.         getline(*addr);
  871.         pline(lineno(addr));
  872.         if (inopen)
  873.             ex_putchar('\n' | QUOTE);
  874.         if (movedot)
  875.             dot = addr;
  876.     }
  877. }
  878.  
  879. pofix()
  880. {
  881.  
  882.     if (inopen && Outchar != termchar) {
  883.         vnfl();
  884.         setoutt();
  885.     }
  886. }
  887.  
  888. /*
  889.  * Dudley doright to the rescue.
  890.  * Undo saves the day again.
  891.  * A tip of the hatlo hat to Warren Teitleman
  892.  * who made undo as useful as do.
  893.  *
  894.  * Command level undo works easily because
  895.  * the editor has a unique temporary file
  896.  * index for every line which ever existed.
  897.  * We don't have to save large blocks of text,
  898.  * only the indices which are small.  We do this
  899.  * by moving them to after the last line in the
  900.  * line buffer array, and marking down info
  901.  * about whence they came.
  902.  *
  903.  * Undo is its own inverse.
  904.  */
  905. undo(c)
  906.     bool c;
  907. {
  908.     register int i;
  909.     register line *jp, *kp;
  910.     line *dolp1, *newdol, *newadot;
  911.  
  912. #ifdef TRACE
  913.     if (trace)
  914.         vudump("before undo");
  915. #endif
  916.     if (inglobal && inopen <= 0)
  917.         error("Can't undo in global@commands");
  918.     if (!c)
  919.         somechange();
  920.     pkill[0] = pkill[1] = 0;
  921.     change();
  922.     if (undkind == UNDMOVE) {
  923.          /*
  924.          * Command to be undone is a move command.
  925.          * This is handled as a special case by noting that
  926.          * a move "a,b m c" can be inverted by another move.
  927.          */
  928.         if ((i = (jp = unddel) - undap2) > 0) {
  929.             /*
  930.              * when c > b inverse is a+(c-b),c m a-1
  931.              */
  932.             addr2 = jp;
  933.             addr1 = (jp = undap1) + i;
  934.             unddel = jp-1;
  935.         } else {
  936.             /*
  937.              * when b > c inverse is  c+1,c+1+(b-a) m b
  938.              */
  939.             addr1 = ++jp;
  940.             addr2 = jp + ((unddel = undap2) - undap1);
  941.         }
  942.         kp = undap1;
  943.         move1(0, unddel);
  944.         dot = kp;
  945.         Command = "move";
  946.         killed();
  947.     } else {
  948.         int cnt;
  949.  
  950.         newadot = dot;
  951.         cnt = lineDOL();
  952.         newdol = dol;
  953.         dolp1 = dol + 1;
  954.         /*
  955.          * Command to be undone is a non-move.
  956.          * All such commands are treated as a combination of
  957.          * a delete command and a append command.
  958.          * We first move the lines appended by the last command
  959.          * from undap1 to undap2-1 so that they are just before the
  960.          * saved deleted lines.
  961.          */
  962.         if ((i = (kp = undap2) - (jp = undap1)) > 0) {
  963.             if (kp != dolp1) {
  964.                 reverse(jp, kp);
  965.                 reverse(kp, dolp1);
  966.                 reverse(jp, dolp1);
  967.             }
  968.             /*
  969.              * Account for possible backward motion of target
  970.              * for restoration of saved deleted lines.
  971.              */
  972.             if (unddel >= jp)
  973.                 unddel -= i;
  974.             newdol -= i;
  975.             /*
  976.              * For the case where no lines are restored, dot
  977.              * is the line before the first line deleted.
  978.              */
  979.             dot = jp-1;
  980.         }
  981.         /*
  982.          * Now put the deleted lines, if any, back where they were.
  983.          * Basic operation is: dol+1,unddol m unddel
  984.          */
  985.         if (undkind == UNDPUT) {
  986.             unddel = undap1 - 1;
  987.             squish();
  988.         }
  989.         jp = unddel + 1;
  990.         if ((i = (kp = unddol) - dol) > 0) {
  991.             if (jp != dolp1) {
  992.                 reverse(jp, dolp1);
  993.                 reverse(dolp1, ++kp);
  994.                 reverse(jp, kp);
  995.             }
  996.             /*
  997.              * Account for possible forward motion of the target
  998.              * for restoration of the deleted lines.
  999.              */
  1000.             if (undap1 >= jp)
  1001.                 undap1 += i;
  1002.             /*
  1003.              * Dot is the first resurrected line.
  1004.              */
  1005.             dot = jp;
  1006.             newdol += i;
  1007.         }
  1008.         /*
  1009.          * Clean up so we are invertible
  1010.          */
  1011.         unddel = undap1 - 1;
  1012.         undap1 = jp;
  1013.         undap2 = jp + i;
  1014.         dol = newdol;
  1015.         netchHAD(cnt);
  1016.         if (undkind == UNDALL) {
  1017.             dot = undadot;
  1018.             undadot = newadot;
  1019.         } else
  1020.             undkind = UNDCHANGE;
  1021.     }
  1022.     /*
  1023.      * Defensive programming - after a munged undadot.
  1024.      * Also handle empty buffer case.
  1025.      */
  1026.     if ((dot <= zero || dot > dol) && dot != dol)
  1027.         dot = one;
  1028. #ifdef TRACE
  1029.     if (trace)
  1030.         vudump("after undo");
  1031. #endif
  1032. }
  1033.  
  1034. /*
  1035.  * Be (almost completely) sure there really
  1036.  * was a change, before claiming to undo.
  1037.  */
  1038. somechange()
  1039. {
  1040.     register line *ip, *jp;
  1041.  
  1042.     switch (undkind) {
  1043.  
  1044.     case UNDMOVE:
  1045.         return;
  1046.  
  1047.     case UNDCHANGE:
  1048.         if (undap1 == undap2 && dol == unddol)
  1049.             break;
  1050.         return;
  1051.  
  1052.     case UNDPUT:
  1053.         if (undap1 != undap2)
  1054.             return;
  1055.         break;
  1056.  
  1057.     case UNDALL:
  1058.         if (unddol - dol != lineDOL())
  1059.             return;
  1060.         for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
  1061.             if ((*ip &~ 01) != (*jp &~ 01))
  1062.                 return;
  1063.         break;
  1064.  
  1065.     case UNDNONE:
  1066.         error("Nothing to undo");
  1067.     }
  1068.     error("Nothing changed|Last undoable command didn't change anything");
  1069. }
  1070.  
  1071. /*
  1072.  * Map command:
  1073.  * map src dest
  1074.  */
  1075. mapcmd(un, ab)
  1076.     int un;    /* true if this is unmap command */
  1077.     int ab;    /* true if this is abbr command */
  1078. {
  1079.     char lhs[100], rhs[100];    /* max sizes resp. */
  1080.     register char *p;
  1081.     register int c;        /* mjm: char --> int */
  1082.     char *dname;
  1083.     struct maps *mp;    /* the map structure we are working on */
  1084.  
  1085.     mp = ab ? abbrevs : exclam() ? immacs : arrows;
  1086.     if (skipend()) {
  1087.         int i;
  1088.  
  1089.         /* print current mapping values */
  1090.         if (peekchar() != EOF)
  1091.             ignchar();
  1092.         if (un)
  1093.             error("Missing lhs");
  1094.         if (inopen)
  1095.             pofix();
  1096.         for (i=0; mp[i].mapto; i++)
  1097.             if (mp[i].cap) {
  1098.                 lprintf("%s", mp[i].descr);
  1099.                 ex_putchar('\t');
  1100.                 lprintf("%s", mp[i].cap);
  1101.                 ex_putchar('\t');
  1102.                 lprintf("%s", mp[i].mapto);
  1103.                 putNFL();
  1104.             }
  1105.         return;
  1106.     }
  1107.  
  1108.     ignore(skipwh());
  1109.     for (p=lhs; ; ) {
  1110.         c = ex_getchar();
  1111.         if (c == CTRL(v)) {
  1112.             c = ex_getchar();
  1113.         } else if (!un && any(c, " \t")) {
  1114.             /* End of lhs */
  1115.             break;
  1116.         } else if (endcmd(c) && c!='"') {
  1117.             ungetchar(c);
  1118.             if (un) {
  1119.                 newline();
  1120.                 *p = 0;
  1121.                 addmac(lhs, NOSTR, NOSTR, mp);
  1122.                 return;
  1123.             } else
  1124.                 error("Missing rhs");
  1125.         }
  1126.         *p++ = c;
  1127.     }
  1128.     *p = 0;
  1129.  
  1130.     if (skipend())
  1131.         error("Missing rhs");
  1132.     for (p=rhs; ; ) {
  1133.         c = ex_getchar();
  1134.         if (c == CTRL(v)) {
  1135.             c = ex_getchar();
  1136.         } else if (endcmd(c) && c!='"') {
  1137.             ungetchar(c);
  1138.             break;
  1139.         }
  1140.         *p++ = c;
  1141.     }
  1142.     *p = 0;
  1143.     newline();
  1144.     /*
  1145.      * Special hack for function keys: #1 means key f1, etc.
  1146.      * If the terminal doesn't have function keys, we just use #1.
  1147.      */
  1148.     if (lhs[0] == '#') {
  1149.         char *fnkey;
  1150.         char *fkey();
  1151.         char funkey[3];
  1152.  
  1153.         fnkey = fkey(lhs[1] - '0');
  1154.         funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
  1155.         if (fnkey)
  1156.             strcpy(lhs, fnkey);
  1157.         dname = funkey;
  1158.     } else {
  1159.         dname = lhs;
  1160.     }
  1161.     addmac(lhs,rhs,dname,mp);
  1162. }
  1163.  
  1164. /*
  1165.  * Add a macro definition to those that already exist. The sequence of
  1166.  * chars "src" is mapped into "dest". If src is already mapped into something
  1167.  * this overrides the mapping. There is no recursion. Unmap is done by
  1168.  * using NOSTR for dest.  Dname is what to show in listings.  mp is
  1169.  * the structure to affect (arrows, etc).
  1170.  */
  1171. addmac(src,dest,dname,mp)
  1172.     register char *src, *dest, *dname;
  1173.     register struct maps *mp;
  1174. {
  1175.     register int slot, zer;
  1176.  
  1177. #ifdef TRACE
  1178.     if (trace)
  1179.         fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
  1180. #endif
  1181.     if (dest && mp==arrows) {
  1182.         /* Make sure user doesn't screw himself */
  1183.         /*
  1184.          * Prevent tail recursion. We really should be
  1185.          * checking to see if src is a suffix of dest
  1186.          * but this makes mapping involving escapes that
  1187.          * is reasonable mess up.
  1188.          */
  1189.         if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
  1190.             error("No tail recursion");
  1191.         /*
  1192.          * We don't let the user rob himself of ":", and making
  1193.          * multi char words is a bad idea so we don't allow it.
  1194.          * Note that if user sets mapinput and maps all of return,
  1195.          * linefeed, and escape, he can screw himself. This is
  1196.          * so weird I don't bother to check for it.
  1197.          */
  1198.         if (isalpha(src[0]) && src[1] || any(src[0],":"))
  1199.             error("Too dangerous to map that");
  1200.     }
  1201.     else if (dest) {
  1202.         /* check for tail recursion in input mode: fussier */
  1203.         if (eq(src, dest+strlen(dest)-strlen(src)))
  1204.             error("No tail recursion");
  1205.     }
  1206.     /*
  1207.      * If the src were null it would cause the dest to
  1208.      * be mapped always forever. This is not good.
  1209.      */
  1210.     if (src == NOSTR || src[0] == 0)
  1211.         error("Missing lhs");
  1212.  
  1213.     /* see if we already have a def for src */
  1214.     zer = -1;
  1215.     for (slot=0; mp[slot].mapto; slot++) {
  1216.         if (mp[slot].cap) {
  1217.             if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
  1218.                 break;    /* if so, reuse slot */
  1219.         } else {
  1220.             zer = slot;    /* remember an empty slot */
  1221.         }
  1222.     }
  1223.  
  1224.     if (dest == NOSTR) {
  1225.         /* unmap */
  1226.         if (mp[slot].cap) {
  1227.             mp[slot].cap = NOSTR;
  1228.             mp[slot].descr = NOSTR;
  1229.         } else {
  1230.             error("Not mapped|That macro wasn't mapped");
  1231.         }
  1232.         return;
  1233.     }
  1234.  
  1235.     /* reuse empty slot, if we found one and src isn't already defined */
  1236.     if (zer >= 0 && mp[slot].mapto == 0)
  1237.         slot = zer;
  1238.  
  1239.     /* if not, append to end */
  1240.     if (slot >= MAXNOMACS)
  1241.         error("Too many macros");
  1242.     if (msnext == 0)    /* first time */
  1243.         msnext = mapspace;
  1244.     /* Check is a bit conservative, we charge for dname even if reusing src */
  1245.     if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
  1246.         error("Too much macro text");
  1247.     CP(msnext, src);
  1248.     mp[slot].cap = msnext;
  1249.     msnext += strlen(src) + 1;    /* plus 1 for null on the end */
  1250.     CP(msnext, dest);
  1251.     mp[slot].mapto = msnext;
  1252.     msnext += strlen(dest) + 1;
  1253.     if (dname) {
  1254.         CP(msnext, dname);
  1255.         mp[slot].descr = msnext;
  1256.         msnext += strlen(dname) + 1;
  1257.     } else {
  1258.         /* default descr to string user enters */
  1259.         mp[slot].descr = src;
  1260.     }
  1261. }
  1262.  
  1263. /*
  1264.  * Implements macros from command mode. c is the buffer to
  1265.  * get the macro from.
  1266.  */
  1267. cmdmac(c)
  1268. char c;
  1269. {
  1270.     char macbuf[BUFSIZ];
  1271.     line *ad, *a1, *a2;
  1272.     char *oglobp;
  1273.     short pk;
  1274.     bool oinglobal;
  1275.  
  1276.     lastmac = c;
  1277.     oglobp = globp;
  1278.     oinglobal = inglobal;
  1279.     pk = peekc; peekc = 0;
  1280.     if (inglobal < 2)
  1281.         inglobal = 1;
  1282.     regbuf(c, macbuf, sizeof(macbuf));
  1283.     a1 = addr1; a2 = addr2;
  1284.     for (ad=a1; ad<=a2; ad++) {
  1285.         globp = macbuf;
  1286.         dot = ad;
  1287.         commands(1,1);
  1288.     }
  1289.     globp = oglobp;
  1290.     inglobal = oinglobal;
  1291.     peekc = pk;
  1292. }
  1293.